/**
 * Created by Administrator on 2017/7/20.
 */

var pomelo = require('pomelo');
var MatchConfig = require('../../../config/match.json');
var gameUtil = require("../../util/gameUtil");
var roomManager = require('../../logic/world/roomManager');

var VirtualSession = require('./VirtualSession').VirtualSession;


/**
 * 比赛场管理类
 * 术语解释：
 * matchId：配置的比赛编号，取值为1-1000
 * matchSN：比赛场次流水号，取值为100000-999999
 * @constructor
 */
function MatchManager() {
    this._matchs = {
        /*
        $matchSN: {
            matchId: 0,
            matchSN: 0,
            startTime: 0,
            step: 1,
            mark: 0,

            users: {
                $uid: {
                    lastRoundScore: 0, // 上一局获胜积分
                    score: 0,          // 积分
                    rank: 0,           // 排名
                    status: 0,         // 状态 1/2/3 正常/淘汰/弃权
                    updateTime: 0,     // 更新时间
                }
            },
            rooms: {
                $roomId: []
            }
        }
        */
    };

    this._users = { // 比赛玩家
        /*
         $uid: {
            matchId: 0,     // 比赛编号
            matchSN: 0,     // 比赛流水号，比赛开始后设值
            roomId: 0       // 所在房间，如果正在游戏则不为0
        }
        */
    };

    this._enrolls = {   // 报名场次
        /*
        $matchId: {
            users : [],         // 报名场次中的玩家数量
            startTime : 0,      // 报名场次的开始时间
            timer : 0           // 报名场次的定时器
        }    // 各场次下的报名玩家
         */
    };
}

MatchManager.getMatchConfigUserCount = function(config) {
    return config.steps[0];
};

MatchManager.prototype = {
    init: function(callback) {
        callback && callback();
    },

    /**
     * 获取（已报名参赛的）玩家信息
     * @param uid
     * @returns {boolean}
     */
    getUser: function(uid) {
        return this._users[uid];
    },

    /**
     * 获取（已报名参赛的）玩家赛场
     * @param uid
     * @returns {*}
     */
    getUserMatchId: function(uid) {
        var user = this._users[uid];
        return (user) ? user.matchId : 0;
    },

    /**
     * 正在进行中的比赛信息
     * @param matchSN
     * @returns {*}
     */
    getMatch: function(matchSN) {
        return this._matchs[matchSN];
    },

    /**
     * 赛场报名信息
     * @param matchId
     * @returns {*}
     */
    getEnroll: function(matchId) {
        return this._enrolls[matchId];
    },

    /**
     * 获取玩家比赛信息
     * @param uid
     * @returns {*}
     */
    getUserMatchInfo: function(uid) {
        var user = this._users[uid];
        if (!user) {
            return;
        }

        var match = this._matchs[user.matchSN];
        if (!match) {
            return;
        }

        return match.users[uid];
    },

    /**
     *
     * 获取结束的时间戳
     * @param matchId
     */
    getEndStamp: function (matchId) {

        var config = MatchConfig[matchId];
        var enroll = this.getEnroll(matchId);

        if (!enroll) {
            return null;
        }

        var endStamp = enroll.startTime + config.overTimeOut;

        return endStamp || null;
    },

    /**
     * 获取某赛场已报名玩家数量
     * @param matchId
     * @returns {number}
     */
    getEnrollUserCount: function(matchId) {
        var enrollUsers = this._enrolls[matchId];

        if (!enrollUsers) {
            return 0;
        }

        return (enrollUsers.users) ? enrollUsers.users.length : 0;
    },

    /**
     * 返回对应报名场次的玩家集合
     * @param matchId
     * @returns {*}
     */
    getEnrollUser: function (matchId) {

        var enrollUsers = this._enrolls[matchId];

        if (!enrollUsers) {
            return {};
        }

        return enrollUsers.users || {};

    },

    /**
     * 获取玩家数据
     * @param result
     * @param callback
     */
    getPlayerInfo: function (result, callback, resp) {

        // 获取
        var uidArray = [];

        // 根据每个玩家获取对应的uid
        result.forEach (function (match) {
            ExObject.each (match.users, function (k, v) {
                uidArray.push (parseInt(k));
            })
        });

        // 将对应uid的数据获取出来
        var resultJson = {};
        var condition = {"_id" : {$in : uidArray}};
        DB.user.find (condition, {"info.name": 1}).toArray(function(err, items){

            if (err) {
                console.log ("读取玩家数据错误: " + err);
                return;
            }

            items.forEach (function (item) {
                resultJson[item._id] = item["info"]["name"];
            });

            // 将玩家的昵称放入比赛列表中
            // 根据每个玩家获取对应的uid
            result.forEach (function (match) {
                ExObject.each (match.users, function (k, v) {
                    v["nick"] = resultJson[parseInt(k)];
                })
            });

            callback (null, resp);
        });
    },

    /**
     * 更新比赛场标记
     * @param mark
     * @param matchSN
     * @param callback
     */
    updateMatchMark: function (mark, matchSN, callback) {

        var sets = {"mark" : mark};

        DB.match.update ({"_id": matchSN}, {'$set': sets}, function (err, result) {
            if (err) {
                callback (err, {Code: err});
                return;
            }

            callback (null, {code: Code.SUCCESS});
        });
    },

    /**
     * 报名之后超时处理函数
     * @param matchId
     */
    enrollOverTime: function (matchId) {

        // 获取到对应的match 所有玩家进行退出
        var players = ExObject.cloneArray(this.getEnrollUser (matchId));

        if (!players) {
            return;
        }

        // 逐个对玩家进行资金回退
        var addResource = MatchConfig[matchId].enrollCost;

        if (!addResource) {
            return;
        }

        // 用于遍历
        players.forEach (function (uid) {

            gPlayerManger.getPlayer(uid, function (player) {
                if (!player) {
                    return;
                }

                player.addResource(addResource);

                var code = this.leaveEnroll (player.uid);
                if (code == Code.SUCCESS) {
                    // 告知对应玩家离开游戏
                    player.send ("onExitMatchEnroll", {
                        code : Code.MATCH_RANK_OVERTIME
                    });
                }
            }.bind (this));
        }, this);
    },

    /**
     * 获取具体的场次
     * @param matchId
     * @returns {*}
     */
    getEnrollRound: function (matchId) {
        return this._enrolls[matchId];
    },

    /**
     * 报名
     * @param matchId
     * @param uid
     * @returns {*}
     */
    enroll: function(matchId, uid) {
        var config = MatchConfig[matchId];
        if (!config) {
            return Code.MATCH_ID_INVALID;
        }

        if (this._users.hasOwnProperty(uid)) {
            return Code.MATCH_ENROLL_REPEAT;
        }

        var enrollUsers = this._enrolls[matchId];
        if (!enrollUsers) {
            enrollUsers = {
                users : [],
                startTime : new Date().getTime(),
                timer : 0
            };

            // 根据第一个玩家的报名时间开始定时
            enrollUsers.timer = setTimeout (function() {
                this.enrollOverTime(matchId);
            }.bind (this), config.overTimeOut);

            this._enrolls[matchId] = enrollUsers;
        }

        var needUserCount = MatchManager.getMatchConfigUserCount(config);
        if (this.getEnrollUserCount (matchId) >= needUserCount) {
            return Code.MATCH_ENROLL_FULL;
        }

        // 获取
        var enrollUser = this.getEnrollUser (matchId);
        enrollUser.push(uid);
        this._users[uid] = {
            matchId: matchId,
            matchSN: 0,
            roomId: 0
        };

        gSessionManager.broadcast(enrollUser, "onUserMatchEnroll", {
            matchId: matchId,
            userCount: enrollUser.length
        });

        return Code.SUCCESS;
    },

    /**
     * 退出报名
     * @param uid
     * @returns {*}
     */
    leaveEnroll: function(uid) {
        var matchUser = this._users[uid];
        if (!matchUser) {
            return Code.SUCCESS;
        }

        // 已开始了不允许退出
        if (matchUser.matchSN) {
            return Code.MATCH_STARTED;
        }

        delete this._users[uid];

        var enrollUsers = this.getEnrollUser(matchUser.matchId);
        if (!enrollUsers) {
            return;
        }

        enrollUsers.remove(uid);

        if (enrollUsers.length < 1) {
            delete this._enrolls[matchUser.matchId];
        }

        return Code.SUCCESS;
    },

    tryStartMatch: function(matchId) {
        var config = MatchConfig[matchId];

        var enrollUsers = this.getEnrollUser (matchId);
        if (!enrollUsers) {
            return;
        }

        var needUserCount = MatchManager.getMatchConfigUserCount(config);
        if (enrollUsers.length < needUserCount) {
            return;
        }

        // 已经可以开始了 则清除倒计时
        clearTimeout(this._enrolls[matchId].timer);

        delete this._enrolls[matchId];
        this._startMatch(matchId, enrollUsers);
    },

    _startMatch: function(matchId, enrollUsers) {
        var self = this;
        var config = MatchConfig[matchId];

        // 添加比赛场
        var match = {
            matchId: matchId,
            matchSN: gGlobalManager.allocMatchSN(matchId),
            startTime: new Date().getTime(),
            step: 1,
            mark: 0,
            users: {},
            rooms: {}
        };

        enrollUsers.forEach(function(uid) {
            match.users[uid] = {
                uid: uid,
                lastRoundScore: 0,
                score: 0,
                rank: 0,
                status: Def.MatchUserStatus.NORMAL,
                updateTime: 0
            };

            var user = self._users[uid];
            if (user) {
                user.matchSN = match.matchSN;
            }
        });

        this._matchs[match.matchSN] = match;

        gSessionManager.broadcast(enrollUsers, "onMatchStart", {
            matchId: match.matchId,
            matchSN: match.matchSN,
            step: match.step
        });

        this._distRoom(match, enrollUsers, function(err) {});
    },


    /**
     * 分配房间号
     * @param match
     * @param uidArray
     * @param callback(err, )
     * @private
     */
    _distRoom: function(match, uidArray, callback) {
        var config = MatchConfig[match.matchId];
        var self = this;

        match.rooms = {};

        // 创建房间
        var initArgs = {
            gameType: config.gameType,
            subType: config.subType,
            round: 1,
            options: config.options,
            matchId: match.matchId,
            matchSN: match.matchSN
        };

        var SubLogic = gameUtil.getSubLogic(initArgs.gameType);
        if (!SubLogic) {
            callback(Code.GAME_TYPE_INVALID); return;
        }

        var subConfig = SubLogic.Config.SubType[initArgs.subType];
        if (!subConfig) {
            callback(Code.SUB_TYPE_INVALID); return;
        }

        var roomPlayerNum = gameUtil.calcRoomMaxPlayer(subConfig, initArgs);
        var roomNum = Math.ceil(uidArray.length/roomPlayerNum);
        var roomUsers = {};
        var roomIdArray = [];
        var isDistUidArray = [];

        for (var i = 0; i < roomNum; ++i) {
            var roomId = roomManager.addMatchRoom(initArgs.gameType, initArgs.subType, initArgs.matchId);
            var roomUidArray = [];

            //for (var j = i*roomPlayerNum, jm = Math.min(j + roomPlayerNum, uidArray.length); j < jm; ++j) {
            //    roomUidArray.push(uidArray[j]);
            //}

            // 要求随机将玩家放入
            while (roomUidArray.length < roomPlayerNum) {
                var uid = Ext.randArray(uidArray);
                if (roomUidArray.indexOf (uid) == -1
                    && isDistUidArray.indexOf (uid) == -1) {
                    roomUidArray.push (uid);
                    isDistUidArray.push (uid);
                }
            }

            roomUsers[roomId] = roomUidArray;
            roomIdArray.push(roomId);
        }

        async.each(roomIdArray, function(roomId, cb) {
            var roomUidArray = roomUsers[roomId];
            var rid = +roomId;

            var session = new VirtualSession(0);
            session.set('rid', rid);

            var createRoomMsg = clone(initArgs);
            createRoomMsg.rid = rid;

            pomelo.app.rpc.game.roomRemote.create(session, 0, createRoomMsg, function (code) {
                if (code) {
                    roomManager.delRoom(rid);
                    cb(code);
                    return;
                }

                match.rooms[rid] = roomUidArray;
                roomUidArray.forEach(function(uid) {
                    var user = self._users[uid];
                    if (user) {
                        user.roomId = rid;
                    }
                });

                gSessionManager.broadcast(roomUidArray, "onMatchRoomStart", {
                    matchId: match.matchId,
                    matchSN: match.matchSN,
                    roomId: rid
                });

                cb();
            });
        }, function(err) {
            callback();

            if (err) {
                console.log ("当前的 错误码:" + err);
            }

            logger.debug("matchManager distRoom matchSN=%d match=%j", match.matchSN, match);
        });
    },


    /**
     * 更新结算
     * @param result
     */
    updateResult: function(result) {
        logger.debug("matchManager updateResult matchSN=%d result=%j", result.matchSN, result);

        var matchSN = result.matchSN;
        var roomId = result.roomId;
        var players = result.players;

        var nowStamp = Date.getStamp();

        var match = this._matchs[matchSN];
        if (!match) {
            return;
        }

        var config = MatchConfig[match.matchId];
        var roomUidArray = match.rooms[roomId];
        if (!roomUidArray) {
            return;
        }

        gSessionManager.broadcast(roomUidArray, "onMatchRoomFinish", {});

        var forceQuitUserArray = []; // 强退玩家
        var normalUserArray = [];    // 正常玩家

        roomUidArray.forEach(function(uid) {
            var matchUser = match.users[uid];
            if (!matchUser) {
                return;
            }

            // 更新玩家数据
            matchUser.updateTime = nowStamp;

            var updateResult = players[uid];
            if (updateResult) {
                matchUser.lastRoundScore = updateResult.roundScore;
            }

            var user = this._users[uid];
            if (user) {
                user.roomId = 0;
            }

            var resultPlayer = result.players[uid];

            // 最后一局不淘汰
            if ((!resultPlayer || resultPlayer.forceQuit)
                && (match.step + 1 <= config.steps.length)) {
                forceQuitUserArray.push(matchUser);
            } else {
                normalUserArray.push(matchUser);
            }
        }.bind(this));

        if (forceQuitUserArray.length > 0) {
            var stepLeftNum = config.steps[Math.min(config.steps.length, match.step) - 1];

            forceQuitUserArray.forEach(function(matchUser) {

                matchUser.status = Def.MatchUserStatus.QUIT;
                matchUser.rank = stepLeftNum;

                gSessionManager.sendToUser(matchUser.uid, "onUpdateMatchResult", {
                    matchId: match.matchId,
                    matchSN: match.matchSN,
                    score: matchUser.score,
                    status: matchUser.status
                });

                this._userMatchFinished(match, matchUser);
            }.bind(this));

            normalUserArray.forEach(function(matchUser) {

                if (matchUser.status != Def.MatchUserStatus.QUIT) {
                    matchUser.score += 1;
                }
            });
        } else {
            normalUserArray.forEach(function(matchUser) {
                var resultPlayer = result.players[matchUser.uid];
                if (resultPlayer) {
                    matchUser.score += resultPlayer.score;
                }
            });
        }

        delete match.rooms[roomId];

        if (ExObject.count(match.rooms) == 0) {
            this._updateStep(match);
        }
    },

    /**
     * 获取排序的顺序
     * @param userArray
     */
    sortPlayerRank: function (userArray) {

        userArray.sort(function(a, b) {

            if (a.status != Def.MatchUserStatus.NORMAL) {
                return a.status - Def.MatchUserStatus.NORMAL;
            }

            if (a.score != b.score) {
                return b.score - a.score;
            }

            if (a.lastRoundScore != b.lastRoundScore) {
                return b.lastRoundScore - a.lastRoundScore;
            }

            return a.updateTime - b.updateTime;
        });

        return userArray;
    },

    _updateStep: function(match) {
        var config = MatchConfig[match.matchId];
        var userArray = [];
        var uidArray = [];

        ExObject.eachKeyNum(match.users, function(uid, user) {
            if (user.status == Def.MatchUserStatus.NORMAL) {
                userArray.push(user);
                uidArray.push(uid);
            }
        });

        // 进行排序
        userArray = this.sortPlayerRank (userArray);

        var newStep = match.step + 1;
        for ( ;newStep <= config.steps.length; ++newStep) {
            if (userArray.length >= config.steps[newStep - 1]) {
                break;
            }
        }

        match.step = newStep;

        var isFinished = (match.step > config.steps.length);
        var leftUidArray = [];
        var leftUserArray = [];

        var stepLeftNum = (isFinished) ? config.steps[config.steps.length - 1] : config.steps[match.step - 1];
        for (var i = 0, m = userArray.length; i < m; ++i) {
            var user = userArray[i];
            user.rank = i + 1;
            if (i < stepLeftNum) {
                leftUidArray.push(user.uid);
                leftUserArray.push(user);
            } else {
                user.status = Def.MatchUserStatus.OUT;
                this._userMatchFinished(match, user);
            }

            gSessionManager.sendToUser(user.uid, "onUpdateMatchResult", {
                matchId: match.matchId,
                matchSN: match.matchSN,
                score: user.score,
                rank: user.rank,
                status: user.status,
                startTime: match.startTime
            });
        }

        logger.debug("matchManager updateStep matchSN=%d match=%j", match.matchSN, match);

        if (match.step > config.steps.length) {
            this._finish(match, leftUserArray, leftUidArray);
        } else {
            this._distRoom(match, leftUidArray, function(err) {});
        }
    },

    _finish: function(match, leftUserArray, leftUidArray) {
        delete this._matchs[match.matchSN];

        var data = clone(match);
        data._id = match.matchSN;
        DB.match.save(data);

        var onMatchFinishedMsg = {
            matchId: match.matchId,
            matchSN: match.matchSN,
            topRanks:[],
            startTime: match.startTime
        };

        async.eachSeries(leftUserArray, function(user, cb) {
            this._userMatchFinished(match, user);
            gPlayerManger.getPlayer(user.uid, function(player) {
                if (player) {
                    onMatchFinishedMsg.topRanks.push({
                        uid: user.uid,
                        rank:user.rank,
                        score:user.score,
                        name:player.user.info.name,
                        headpic:player.user.info.headpic
                    });
                }
                cb();
            });
        }.bind(this), function(err) {
            gSessionManager.broadcast(leftUidArray, "onMatchFinished", onMatchFinishedMsg);
        });
    },

    _userMatchFinished: function(match, matchUser) {
        var config = MatchConfig[match.matchId];

        delete this._users[matchUser.uid];

        logger.debug("matchManager userMatchFinished matchSN=%d matchUser=%j", match.matchSN, matchUser);

        gPlayerManger.getPlayer(matchUser.uid, function(player) {
            var user = player.user;
            user.match = {
                matchSN: match.matchSN,
                rank: matchUser.rank,
                reward: 0,
                startTime: match.startTime
            };
            player.markDirty("match");

            if (matchUser.status != Def.MatchUserStatus.QUIT) {
                var reward = config.reward[matchUser.rank];
                if (reward) {
                    player.addResource(reward, "matchReward");
                }
            }
        });
    }
};

exports.MatchManager = MatchManager;